/** Screen.cs - A simple Game Screen Interface
 * @author - Spencer Evans
 * @attribution - Similar to a GameScreen class found in the Game State Management sample on the 
 *  XNA Creators Club Online website (http://creators.xna.com).
 */


#region Using Statements
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
#endregion


namespace BenevolentSun
{
    /** Enum describes the screen transition state.
     */ 
    public enum ScreenState
    {
        TransitionOn,
        Active,
        TransitionOff,
        Hidden,
    }


    /** The Screen class is an interface for game screens that can be managed 
     * by the screen manager. Screens can handle transitioning on and off and removing
     * themselves from the manager.
     */
    public class Screen
    {
        public event EventHandler OnExiting;

        public event EventHandler OnRemove;


        #region Properties
        /** Gets the manager that this screen belongs to. */
        ScreenManager screenManager;
        public ScreenManager ScreenManager
        {
            get { return screenManager; }
            internal set { screenManager = value; }
        }
        /** Notes the current ScreenState of the screen. */
        ScreenState screenState = ScreenState.TransitionOn;
        virtual public ScreenState ScreenState
        {
            get { return screenState; }
            protected set { screenState = value; }
        }
        /** If this window is a popup, then it will not cause windows below it in the stack to be flagged as occluded. */
        bool isPopup = false;
        public bool IsPopup
        {
            get { return isPopup; }
            protected set { isPopup = value; }
        }

        bool isModal = true;
        public bool IsModal
        {
            get { return isModal; }
            set { isModal = value; }
        }

        /** Notes weather or not the window is exiting after a transition off completes. */
        bool isExiting = false;
        public bool IsExiting
        {
            get { return isExiting; }
            protected internal set { isExiting = value; }
        }
        /** Notes if the focus was available for this screen at that last update. */
        bool isFocusAvailable = false;
        public bool IsActive
        {
            get
            {
                return isFocusAvailable &&
                       (screenState == ScreenState.TransitionOn ||
                        screenState == ScreenState.Active);
            }
        }

        /** Indicates how long the screen takes to transition on when it is activated. */
        TimeSpan transitionOnTime = TimeSpan.Zero;
        public TimeSpan TransitionOnTime
        {
            get { return transitionOnTime; }
            protected set { transitionOnTime = value; }
        }
        /** Indicates how long the screen takes to transition off when it is deactivated. */
        TimeSpan transitionOffTime = TimeSpan.Zero;
        public TimeSpan TransitionOffTime
        {
            get { return transitionOffTime; }
            protected set { transitionOffTime = value; }
        }
        /** A transitioning interpolator that can be used to transitioning on and off effects. */
        float transitionPosition = 1;
        public float TransitionPosition
        {
            get { return transitionPosition; }
            protected set { transitionPosition = value; }
        }
        /** Helper to find out what the transitioning position would be for a 0-255 range alpha value. */
        public byte TransitionAlpha
        {
            get { return (byte)(255 - TransitionPosition * 255); }
        }

        public ContentManager Content
        {
            get { return screenManager.Game.Content; }
        }
        public SpriteBatch SpriteBatch
        {
            get { return screenManager.SpriteBatch; }
        }
        public GraphicsDevice GraphicsDevice
        {
            get { return screenManager.GraphicsDevice; }
        }
        #endregion



        #region Initialization
        public Screen()
        {
            
        }
        public virtual void LoadContent() { }
        public virtual void UnloadContent() { }
        #endregion



        #region Virtual Methods
        #region Update Methods
        // Update and Draw
        public virtual void Update(GameTime gameTime, bool isFocusAvailable, bool isOccluded)
        {
            // Store the focus state
            this.isFocusAvailable = isFocusAvailable;

            // Update based on our screen state
            if (IsExiting)
            {
                // If the screen is going away to die, it should transition off.
                screenState = ScreenState.TransitionOff;

                // Update the turning off transition and when the transition finishes, remove the screen.
                if (!UpdateTransition(gameTime, transitionOffTime, 1))
                    Remove();
            }
            else if (isOccluded)
            {
                // If the screen is covered by another, it should transition off to hide itself so its not drawn
                if (UpdateTransition(gameTime, transitionOffTime, 1))
                    screenState = ScreenState.TransitionOff;    // Still busy transitioning.
                else
                    screenState = ScreenState.Hidden;           // Transition finished!
            }
            else
            {
                // Otherwise the screen should transition on and become active if not so already
                if (UpdateTransition(gameTime, transitionOnTime, -1))
                    screenState = ScreenState.TransitionOn;     // Still busy transitioning.
                else
                    screenState = ScreenState.Active;           // Transition finished!
            }
        }

        #endregion


        protected virtual void Remove()
        {
            if (OnRemove != null)
                OnRemove(this, EventArgs.Empty);
            ScreenManager.RemoveScreen(this);
        }


        #region InputMethods
        /** Allows the screen to handle user input. Unlike Update, this method
         * is only called when the screen is active, and not when some other
         * screen has taken the focus.
         */
        public virtual void HandleInput() {
            if (!IsActive)
                return;
        }
        #endregion



        #region Draw Methods
        /** This is called when the screen should draw itself.
         */
        public virtual void Draw(GameTime gameTime) {
            
        }
        #endregion
        #endregion



        #region Public Methods
        /** Tells the screen to go away. Unlike ScreenManager.RemoveScreen, which
         * instantly kills the screen, this method respects the transition timings
         * and will give the screen a chance to gradually transition off.
         */
        public virtual void ExitScreen()
        {
            // flag that it should transition off and then exit.
            IsExiting = true;

            if (OnExiting != null)
                OnExiting(this, new EventArgs());

            // If the screen has a zero transition time, remove it immediately.
            //if (TransitionOffTime == TimeSpan.Zero)
            //{
                //ScreenManager.RemoveScreen(this);
            //}
        }

        
        #endregion




        /** Updates the transition interpolator position, does not actually make any transition changes.
         * @return - Returns true if there is still transitioning to be done, otherwise false.
         */
        virtual protected bool UpdateTransition(GameTime gameTime, TimeSpan time, int direction)
        {
            // Find out how much transitioning should be done this update
            float transitionDelta;
            if (time == TimeSpan.Zero)
                transitionDelta = 1;
            else
                transitionDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds /
                                          time.TotalMilliseconds);

            // Update the transition position
            transitionPosition += transitionDelta * direction;

            // Did we reach the end of the transition?
            if ((transitionPosition <= 0) || (transitionPosition >= 1))
            {
                transitionPosition = MathHelper.Clamp(transitionPosition, 0, 1);
                return false;
            }

            // Otherwise we are still busy transitioning.
            return true;
        }

    }
}
